--------------------------------------
--   Matias Blanco & Sergio Perez   --
-- Top Manta - Inventadas ADA Citas --
--------------------------------------

with Ada.Text_Io ; use Ada.Text_Io;
with Ada.Integer_Text_Io; use Ada.Integer_Text_Io;
with Ada.Numerics.Float_Random ; use Ada.Numerics.Float_Random;
with Ada.Numerics.Discrete_Random;


procedure TopMantaCitasAda is

   G: Generator;



   -- Constantes
   MAX_PIRATAS: constant Integer:= 10; -- tipos que copian CDs
   MAX_CDs_ALMACEN: constant Integer:= 2000; -- CDs que caben en un almacen

   MAX_CDs_PIDEN: constant Integer:= 200; -- Numero de CDs que piden como mucho
   MIN_CDs_PIDEN: constant Integer:= 100; -- Minimo de CDs que pueden pedir

   MAX_ALMACENES: constant Integer:= 4; -- Almacenes por los que se puede mover la mercancia

   -- Aleatorio discreto

   subtype valores is Integer range 1..MAX_ALMACENES;
   package NumeroAleatorio is new Ada.Numerics.Discrete_Random(valores);

   F: NumeroAleatorio.Generator;

   -- Tipos y Variables

   type TAlmacenesReposicion is array (1..MAX_ALMACENES) of Boolean;

   type TPiratasCitados is array (1..MAX_PIRATAS) of Boolean;

   task Policia;

   task type Almacen is
      entry Init(Idd: in Integer);
      entry Pilla_CDs(Idd: in Integer; N_CDs: in Integer; OK: in out Boolean);
      entry Llenar; -- Para reponer
      entry Vaciar(N_CDs: in out Integer);
      entry Meter(N_CDs: in Integer);
      entry PoliInvestiga(N_CDs: in out Integer); -- La poli quiere investigar
      entry PoliRequisa;
   end Almacen;

   Almacenes: array (1..MAX_ALMACENES) of Almacen;

   task PePing is
      entry Reponer(Id: in Integer; N: in Integer);
      entry Soplo(NumAlmacen: in Integer); -- Id avisa de que viene la poli a ese almacen
      entry Carcel; -- A la carcel, le han pillado, pero se librara chivandose...
   end Peping;

   task type Piratilla is
      entry Init(Idd: in Integer);
   end Piratilla;

   Piratillas: array (1..MAX_PIRATAS) of Piratilla;

   task Citacion is
      entry MandarCitacion(Id: in Integer);
      entry VerTablon(Id: in Integer; Denuncia: in out Boolean);
   end Citacion;

   task body Citacion is
      Denuncias: TPiratasCitados;
   begin
      for I in 1..MAX_PIRATAS loop
         Denuncias(I):= False;
      end loop;
      loop
         select
            accept MandarCitacion(Id: in Integer) do
               Denuncias(Id):= True;
            end MandarCitacion;
         or
            accept VerTablon(Id: in Integer; Denuncia: in out Boolean) do
               Denuncia:=Denuncias(Id);
               Denuncias(Id):=False;
            end VerTablon;
         end select;
      end loop;
   end Citacion;

   task body almacen is
      Id: Integer;
      N_CDS_ALMA: Integer;


   begin
      accept Init(Idd: in Integer) do
         Id:= Idd;
      end Init;

      N_CDS_ALMA:= MAX_CDs_ALMACEN; -- Lleno en un principio

      loop
         select
            accept Pilla_CDs(Idd: in Integer; N_CDs: in Integer; OK: in out Boolean) do
               if (N_CDs <= N_CDs_ALMA) then
                  Put("El piratilla " & Integer'Image(Idd) & " coge " & Integer'Image(N_CDs) & " CDs del almacen " & Integer'Image(Id) );New_Line;
                  N_CDs_ALMA:= N_CDs_ALMA - N_CDs;
                  OK:=True;
               else
                  OK:= False;
               end if;
            end Pilla_CDs;
         or
            accept Llenar do
               if (N_CDs_ALMA >= MAX_CDs_ALMACEN) then
                  Put("El almacen " & Integer'Image(Id) & "ya no hace falta llenarlo.");New_Line;
               else
                  Put("PePing empieza a llenar el almacen " & Integer'Image(Id));New_Line;
                  N_CDs_ALMA:= MAX_CDs_ALMACEN;
                  delay 1.0;
                  Put("PePing termina de llenar el almacen " & Integer'Image(Id));New_Line;
               end if;
            end Llenar;
         or
            accept Vaciar(N_CDs: in out Integer) do
               Put("PePing vacia el almacen " & Integer'Image(Id));New_Line;
               N_CDs:= N_CDs_ALMA;
               N_CDs_ALMA:=0;
            end Vaciar;
         or
            accept Meter(N_CDS: in Integer) do
               Put("PePing mete " & Integer'Image(N_CDS) & " CDs de la mudanza en el almacen " & Integer'Image(Id));New_Line;
               N_CDs_ALMA:= N_CDs_ALMA + N_CDs;
            end Meter;

         or
            accept PoliInvestiga(N_CDs: in out Integer) do
               N_CDs:= N_CDs_ALMA;
            end PoliInvestiga;
         or
            accept PoliRequisa do
               N_CDs_ALMA:=0;
            end PoliRequisa;
         end select;
      end loop;
   end Almacen;

   task body PePing is
      AlmacenesRepo: TAlmacenesReposicion;
      AuxSacar: Integer:= 0;

   begin
      for I in 1..MAX_ALMACENES loop
         AlmacenesRepo(I):= False; -- No hay ninguno en reposicion
      end loop;

      loop
         select
            accept Reponer(Id: in Integer; N:in Integer) do
               if (not AlmacenesRepo(N)) then
                  Put("PePing recibe la llamada del piratilla " & Integer'Image(Id) & " avisando de que no hay CDs sufientes para el en almacen " & Integer'Image(N));New_Line;
                  AlmacenesRepo(N):=True;
                  Almacenes(N).Llenar;
                  AlmacenesRepo(N):=False;
               else
                  Put("PePing recibe la llamada del piratilla " & Integer'Image(Id) & " avisando de que no hay CDs sufientes para el en almacen " & Integer'Image(N) & " y PePing le dice que ya estaba avisado!");New_Line;
               end if;
            end Reponer;


         or
            accept Soplo(NumAlmacen: in Integer) do
               Put("MOVIENDO! La poli filtra a PePing que se va a investigar el almacen " & Integer'Image(NumAlmacen));New_Line;
               Almacenes(NumAlmacen).Vaciar(AuxSacar);
               delay (1.0); -- tarda un poco en mudar
               if (NumAlmacen < MAX_ALMACENES) then
                  Almacenes(NumAlmacen+1).Meter(AuxSacar);
               else
                  Almacenes(1).Meter(AuxSacar);
               end if;
            end Soplo;

         or

            accept Carcel do
               Put("PePing a la carcel, pero... para librarse...");New_Line;
               for I in 1..MAX_PIRATAS loop
                  if (Random(G) < 0.3) then
                     Put("... inculpa y manda al piratilla " & Integer'Image(I) & " a la carcel x(");New_Line;
                     Citacion.MandarCitacion(I);
                  end if;
               end loop;
            end Carcel;
         end select;
      end loop;
   end Peping;

   task body Piratilla is
      Id: Integer;
      N_CDs: Integer;
      OK: Boolean;
      CarcelFlag: Boolean;
      AlmacenActual: Integer:=0;

   begin
      accept Init(Idd: in Integer) do
         Id:= Idd;
         N_CDs:= Integer(Float(MAX_CDS_PIDEN-MIN_CDS_PIDEN)*Random(G))+MIN_CDS_PIDEN;
         CarcelFlag:=False;
      end Init;
      loop
         -- Graba
         Put("El piratilla " & Integer'Image(Id) & " están grabando");New_Line;
         delay Duration(0.1*Float(N_CDs)*Random(G));
         -- Mira si le han pillado-----
         Citacion.VerTablon(Id, CarcelFlag);
         if (CarcelFlag) then
            Put("El pirata " & Integer'Image(Id) & " se va a la carcel");New_Line;
            delay 20.0;
            Put("El pirata " & Integer'Image(Id) & " sale de la carcel");New_Line;
         end if;
         --------
         AlmacenActual:=NumeroAleatorio.Random(F);
         OK:=False;
         Put("El piratilla " & Integer'Image(Id) & " necesita CDs y va al almacen " & Integer'Image(AlmacenActual) & " a ver si puede pillarlos");New_Line;
         while (not OK) loop
            Citacion.VerTablon(Id, CarcelFlag);
            if (CarcelFlag) then
               Put("El piratilla " & Integer'Image(Id) & " se libra de la carcel porque no tiene CDs!");New_Line;
            end if;
            Almacenes(AlmacenActual).Pilla_CDs(Id, N_CDs, OK);
            if (not OK) then
               Put("El piratilla " & Integer'Image(Id) & " avisa a PePing de que no hay CDs suficientes para el en el almacen " & Integer'Image(AlmacenActual));New_Line;
               PePing.Reponer(Id, AlmacenActual);
               Put("*");
            end if;
         end loop;
         Put("El piratilla " & Integer'Image(Id) & " consigue los CDs (");Put(N_CDs,0);Put(")");New_Line;

      end loop;
   end Piratilla;

   task body Policia is
      AlmacenInvestigado: Integer;
      CDsEncautados: Integer;
   begin
      Reset(G);
      AlmacenInvestigado:= 1; -- Empiezan por el primero.
      -- Se dan vueltas por todos los almacenes y deciden o no si van a investigar
      loop
         delay Duration(Random(G) * 3.0 + 3.0); -- Un cafetillo hasta ir a otro almacen.

         CDsEncautados := 0;
         if (Random(G) < 0.8) then
            if (Random(G) < 0.5) then
               PePing.Soplo(AlmacenInvestigado);
               delay 1.0;
            end if;
            Almacenes(AlmacenInvestigado).PoliInvestiga(CDsEncautados);
            if (CDsEncautados > 0) then
               PePing.Carcel; -- Ramoncin estara contento.
               -- Aqui pueden seguir sacando mientras no requisen
               Almacenes(AlmacenInvestigado).PoliRequisa;
               Put("Se requisan " & Integer'Image(CdsEncautados) & " CDs del almacen " & Integer'Image(AlmacenInvestigado));New_Line;            end if;
         end if;


         AlmacenInvestigado:= AlmacenInvestigado + 1;

         if (AlmacenInvestigado = MAX_ALMACENES + 1) then
            AlmacenInvestigado:= 1;
         end if;

      end loop;
   end Policia;


begin
--   Put("Matias Blanco & Sergio Perez - TopManta");New_Line; -- Egocentrismo puro y duro

   Reset(G);

   for I in 1..MAX_ALMACENES loop
      Almacenes(I).Init(I);
   end loop;

   for I in 1..MAX_PIRATAS loop
      Piratillas(I).Init(I);
   end loop;

end TopMantaCitasAda;
